fix(super-editor): stop export from stripping tracked marks off the caller's tree - #3949
Open
leonidkuznetsov18 wants to merge 1 commit into
Open
Conversation
…aller's tree The w:ins/w:del decoders removed the tracked-change mark from the node they were handed by assigning the filtered marks array back onto it. That node belongs to the caller: the body export starts from a fresh getUpdatedJson() snapshot each time, but once a header/footer sub-editor is registered, #exportProcessHeadersFooters serializes the converter's persistent import-time tree (this.headers[id] / this.footers[id]) by reference. The first exportDocx() therefore stripped trackInsert/trackDelete permanently, and every later export wrote the header/footer redline as accepted plain text — a counterparty's tracked deletion silently became regular content on the second save, with no user edit involved. Strip the mark on a shallow copy of the node instead, leaving the caller's tree intact, so repeated exports with no intervening edit produce the same tracked changes every time. Fixes superdoc#3893 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Contributor
|
All contributors have signed the CLA ✍️ ✅ |
Author
|
recheck |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Once a header (or footer) sub-editor exists, the first
exportDocx()writes the part's importedw:ins/w:delcorrectly and every later export writes it with the tracked changes gone — text inside aw:delcomes back as an ordinary run, so a counterparty's tracked deletion silently becomes accepted content on the second save, with no user edit involved (#3893).Mechanism
The
w:ins/w:deldecoders (v3/handlers/w/ins/ins-translator.js,v3/handlers/w/del/del-translator.js) removed the tracked-change mark by assigning the filtered marks array back onto the node they were handed:That node belongs to the caller. The body path never notices because it starts from a fresh
getUpdatedJson()snapshot each export, but#exportProcessHeadersFooterspasses the converter's persistent import-time tree (this.headers[id]/this.footers[id]) by reference once a sub-editor is registered — which happens as soon as a user clicks into the header. Export 1 strips the marks off that tree permanently; export 2 re-serializes the already-stripped tree.Fix
Strip the mark on a shallow copy of the node instead (
{ ...node, marks: filtered }), leaving the caller's tree intact. This fixes the root cause for everyexportToXmlJsoncaller, including theheader-footer-syncfallback path that also passes the persistent tree by reference — repeated exports with no intervening edit now produce the same tracked changes every time. These two assignments are the only in-place mutations of caller data on the export path (auditednode.marks =/node.attrs =/ splice sites in the decode chain).Testing
headerTrackedChangesDoubleExport.test.jswith a synthetic fixture (header-tracked-changes.docx, derived from the in-treeheader-alternating-odd-even.docxwith onew:ins+ onew:delspliced intoword/header1.xml). It registers a header sub-editor the wayHeaderFooterEditorManagerdoes, exports twice, and assertsword/header1.xmlstill carriesins=1 del=1on the second export. Red without the fix (second export drops toins=0 del=0, exactly the issue's output), green with it.marksuntouched while the stripped copy is what reachesexportSchemaToJson.import-export+super-convertersuites: 3643/3643 pass.Fixes #3893